/*
    The NeonX Library

    Copyright (c) 2013 darkcat

    Code covered by the MIT License
*/

#pragma once

// nx...
#include "config/general.h"
// Unequal
#include "utility/operator.h"
// nx::alloc
#include "memory/memalloc.h"
// nx_lock_type
#include "thread/locker.h"
// nx::swap
#include "algorithm/algorithm.h"

//////////////////////////////////////////////////////////////////////////
NX_BEG
//////////////////////////////////////////////////////////////////////////

template <class Alloc_ = NX_ALLOC>
class RefBase : nx_operator(RefBase<Alloc_>, Unequal)
{
protected:
    ulong* ref_;

public:
    RefBase(void) : ref_(NX_NULL) {}

public:
    friend bool operator==(const RefBase& r1, const RefBase& r2)
    {
        return (r1.ref_ == r2.ref_);
    }

    void init(void)
    {
        dec();
        ref_ = nx::alloc<Alloc_, ulong>();
        if (!ref_) return;
        (*ref_) = 1;
    }

    bool set(const RefBase& r)
    {
        if ((*this) != r)
        {
            dec();
            return inc(r);
        }
        else
            return false;
    }

    bool inc(const RefBase& r)
    {
        nx_lock_type(RefBase);
        ref_ = r.ref_;
        if (ref_)
        {
            ++(*ref_);
            return true;
        }
        else
            return false;
    }
    void dec(void)
    {
        nx_lock_type(RefBase);
        if (!ref_ || (*ref_) == 0) return;
        if (--(*ref_) == 0)
        {
            release();
            nx::free<Alloc_>(ref_);
            ref_ = NX_NULL;
        }
    }

    atomic::norm_t ref(void)
    {
        return (ref_ ? (*ref_) : 0);
    }

    RefBase& swap(RefBase& rhs)
    {
        nx::swap(ref_, rhs.ref_);
        return *this;
    }

    virtual void release() = 0;
};

//////////////////////////////////////////////////////////////////////////

template <typename P>
class RefCounter : public P
{
public:
    RefCounter(void) {}

    template <typename T>
    RefCounter(T r)                            { P::set(r); }
    template <typename T, typename U>
    RefCounter(T r, U s)                       { P::set(r, s); }
    RefCounter(const RefCounter& r)            { P::set(r); }

    virtual ~RefCounter()                      { P::dec(); }

public:
    template <typename T>
    RefCounter& operator=(T r)                 { P::set(r); return (*this); }
    RefCounter& operator=(const RefCounter& r) { P::set(r); return (*this); }
};

//////////////////////////////////////////////////////////////////////////
NX_END
//////////////////////////////////////////////////////////////////////////
